package cn.tedu.dao.common;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import cn.tedu.util.PropertyUtil;



/**
 * 数据库操作的公共类
 * 获取连接的方法
 * 关闭资源的方法
 * 通用dml操作    增删改
 * 通用dql操作    查询        反射版本
 * @author Administrator
 *
 */
public class CommonDao {
	//读取属性文件内容
	private static PropertyUtil pu=new PropertyUtil("db.properties");
	private static PropertyUtil puu=new PropertyUtil(pu.getPropertyValue("dbtype")+".properties");
	//下面的变量的值来源于属性文件
	private static String driverClass=puu.getPropertyValue("driverClass");
	private static String url=puu.getPropertyValue("url");
	private static String name=puu.getPropertyValue("name");
	private static String password=puu.getPropertyValue("password");
	/**
	 * 这个方法是一个公共方法,专门用来获取数据库连接对象的
	 * 建议,把方法修饰成static,方便用类名来调用
	 * @return
	 */
	public static Connection getConnection() {
		Connection con=null;
		try {
			//加载第三方类,加载mysql的驱动,即把Driver类加载到方法区中,
			//并在堆区中创建Driver的Class对象
			Class.forName(driverClass);
			//jdbc:mysql://是一种数据传输协议,此协议只能跟mysql服务器传输数据
			con=DriverManager.getConnection(
					url,
					name,
					password);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return con;
	}
	/**
	 * 此方法是公共方法,专门用来关闭数据库资源的
	 * 建议修饰成static
	 * @param rs        结果集资源
	 * @param pstmt     命令资源
	 * @param con       连接资源
	 */
	public static void closeAll(ResultSet rs,
			                    PreparedStatement pstmt,
			                    Connection con) {
		try {
			if(rs!=null) {
				rs.close();
			}
			if(pstmt!=null) {
				pstmt.close();
			}
			if(con!=null) {
				con.close();
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/**
	 * 通用的dml操作  增删改
	 * @param sql       dml的sql语句
	 * @param params    dml的SQL语句的?号的值
	 * @return   受影响的行数
	 */
	public static int executeUpdate(String sql,Object...params)throws SQLException {
		int rowAffect=0;
		//1.获取连接
		Connection con=getConnection();
		//2.构建sql语句     通过方法参数传递进来
		//3.构建sql的执行计划,并给?号赋值
		PreparedStatement pstmt=con.prepareStatement(sql);
		if(params!=null) {
			for(int i=0;i<params.length;i++) {
				pstmt.setObject((i+1), params[i]);
			}
		}
		//4.执行更行
		rowAffect=pstmt.executeUpdate();
		closeAll(null,pstmt,con);
		//5.关闭资源
		return rowAffect;
	}
	/**
	 * 通用查询方法  dql操作  反射版本
	 * @param clazz  实体Class对象 
	 * @param sql      dql语句
 	 * @param params  dql语句的问号值
	 * @return  List集合
	 * 
	 * 泛型方法
	 */
	public static <T> List<T> executeQuery(Class<T> clazz,
			                               String sql,
			                               Object...params)throws Exception {
		List<T> list=new ArrayList<T>();
		//1.获取连接
		Connection con=getConnection();
		//2.构建sql语句   方法的参数传递进来
		//3.构建执行计划,给?号赋值
		PreparedStatement pstmt=con.prepareStatement(sql);
		if(params!=null) {
			for(int i=0;i<params.length;i++) {
				pstmt.setObject((i+1), params[i]);
			}
		}
		//4.执行查询
		ResultSet rs=pstmt.executeQuery();
		//得到结果集rs对象,rs对象中有什么,只有结果集的列头信息
		
		//获取结果集的元数据(metadata)
		ResultSetMetaData rsmd=rs.getMetaData();
		//从结果集的元数据中获取结果集中的所有的列头的名字
		List<String> columnNames=new ArrayList<String>();//id name password  age address
		for(int i=0;i<rsmd.getColumnCount();i++) {
			String columnName=rsmd.getColumnLabel((i+1));
			columnNames.add(columnName);
		}
		//执行到此处在columnNames集合中存储的是所有的结果集列名字
		
		//5.处理结果集
		while(rs.next()) {
			T t=clazz.newInstance();
			//用反射调用T中的set方法,有寻找和调用的过程
			for(String columnName : columnNames) {
				//类似于 id->setId   name->setName  ...
				String setterName="set"+columnName.substring(0,1).toUpperCase()+columnName.substring(1);
				Method[] methods=clazz.getDeclaredMethods();
				for(Method method: methods) {
					if(setterName.equals(method.getName())) {
						//说明找到了T类型中的setter方法,调用这个方法并存储数据
						//从结果集中的当前行和当前columnName列中获取数据
						Object value=rs.getObject(columnName);
						//处理value是聚合函数的值,因为这个值在结果集中Long类型
						if(value instanceof Long) {
							Long l=(Long)value;
							value=l.intValue();
						}
						//处理oralce的id字段是number类型,进入结果集后就变成了BigDecimal类型
						if(value instanceof BigDecimal) {
							BigDecimal bd=(BigDecimal)value;
							value=bd.intValue();
						}
						//调用method这个setter方法,并存储数据
						method.invoke(t, value);
						break;
					}
				}
			}
			list.add(t);//把当前行数据对象存储给list集合
		}
		//6.关闭资源
		closeAll(rs,pstmt,con);
		return list;
	}
}
